feat(serving): config validation, integration coverage, docs refresh (PR 5/5)#110
Merged
feat(serving): config validation, integration coverage, docs refresh (PR 5/5)#110
Conversation
…(PR 5/5) Final PR of the production-readiness series. Closes the remaining audit findings around silent oversized-input clamping, missing end-to-end coverage of the serving filter chain, and stale tech-stack pins. McpTools per-call clamping: - queryNodes / queryEdges `limit` → clamped to mcp.limits.max_results - getEgoGraph `radius` → clamped to mcp.limits.max_depth - searchGraph `limit` → clamped to mcp.limits.max_results Defense-in-depth on top of the transaction-timeout cap from PR 2 — a caller asking for limit=1_000_000 or radius=999 now gets the configured ceiling instead of an unbounded Cypher walk. ConfigValidator hard ceilings + blank-string checks: - mcp.limits.max_payload_bytes — must be > 0 - mcp.limits.rate_per_minute — must be > 0 - mcp.limits.max_depth — must be 1..100 (variable-length Cypher with depth >100 is pathological — DoS sentinel) - mcp.auth.token_env / .token — blank fails validation when mode=bearer ServingChainIntegrationTest (new, 9 cases): - 401 envelope shape with X-Request-Id correlation - 200 + valid token pass-through - 401 + wrong token - security headers present on success responses - inbound X-Request-Id propagation when valid - inbound control-char rejection (log-injection defense) - 429 envelope with Retry-After + X-RateLimit-Remaining: 0 - /actuator/health bypasses auth (kubelet probes) - rate-limit bucket isolation per token Manually chains the four filters via lambda FilterChain instances — runs sub-second, no @SpringBootTest, no Neo4j. CLAUDE.md tech-stack pins: Spring Boot 4.0.5→4.0.6, Spring AI 2.0.0-M3→2.0.0-M4, Neo4j 2026.02.3→2026.04.0; added Bucket4j 8.18.0, logstash-logback-encoder 9.0, micrometer-registry-prometheus. Tests: 3689 / 0 / 0 / 32 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
80c822b to
54c3eba
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final PR of the 5-PR production-readiness series. Closes the remaining audit findings around silent oversized-input clamping, missing end-to-end coverage of the serving filter chain, and stale tech-stack pins in
CLAUDE.md.McpTools.queryNodes/queryEdges/getEgoGraph/searchGraph— caller-suppliedlimitclamped tomcp.limits.max_results,radiusclamped tomcp.limits.max_depth. Defense-in-depth on top of PR 2's transaction-timeout cap so alimit=1_000_000orradius=999request gets the configured ceiling instead of an unbounded Cypher walk.ConfigValidatorhard ceilings + blank-string checks —max_payload_bytes/rate_per_minute/max_depth(1..100 ceiling — variable-length Cypher above 100 is pathological in practice; DoS sentinel) get explicit range validation;mcp.auth.token_env/mcp.auth.tokenblank-string fails validation whenmode=bearerrather than silently coercing to null and fail-fasting at startup with a confusing message.ServingChainIntegrationTest(new, 9 cases) — fills the gap where each filter (RequestIdFilter,SecurityHeadersFilter,RateLimitFilter,BearerAuthFilter) had unit-test coverage in isolation but no test exercised the full chain together. Asserts the cross-filter contract: 401 envelope withrequest_idechoed inX-Request-Idresponse header; 429 envelope withRetry-After+X-RateLimit-Remaining: 0; security headers on every response; inboundX-Request-Idpropagation; control-char rejection (log-injection defense); rate-limit bucket isolation per token;/actuator/healthauth bypass. Manually chains the four filters via lambdaFilterChaininstances — sub-second, no Spring context, no Neo4j.CLAUDE.mdtech-stack pin refresh — Spring Boot 4.0.5→4.0.6, Spring AI 2.0.0-M3→2.0.0-M4, Neo4j 2026.02.3→2026.04.0; added Bucket4j 8.18.0, logstash-logback-encoder 9.0, micrometer-registry-prometheus.Test plan
mvn test -Dtest='ServingChainIntegrationTest'— 9/9 passCloses the production-readiness series
PR 1 (#106 — security baseline), PR 2 (#107 — resource limits), PR 3 (#108 — supply chain & bundle integrity), PR 4 (#109 — observability), PR 5 (this PR — config validation + IT + docs).
🤖 Generated with Claude Code